package tech

import (
	"fmt"
	"slices"
	"ssp/set"
)

type Result struct {
	Chosen       []string   `json:"chosen"`
	Removed      []string   `json:"removed"`
	HelpHouses   [][]string `json:"helpHouses"`
	HelpCells    [][]string `json:"helpCells"`
	HelpValues   [][]string `json:"helpValues"`
	HelpCands    [][]string `json:"helpCands"`
	HelpChains   []string   `json:"helpChains"`
	StrategyName string     `json:"strategyName"`
}

func NewResult() *Result {
	o := &Result{}
	o.Chosen = []string{}
	o.Removed = []string{}
	o.HelpHouses = [][]string{}
	o.HelpCells = [][]string{}
	o.HelpValues = [][]string{}
	o.HelpCands = [][]string{}
	o.HelpChains = []string{}
	o.StrategyName = ""
	return o
}

func (o *Result) setChosen(value int, c *Cell) {
	s := strChosenCell(c.rowNum, c.colNum, value)
	o.Chosen = []string{s}
}

func (o *Result) addRemoved(cands set.Set, cs []*Cell) {
	for _, c := range cs {
		s := strCandsCell(c.rowNum, c.colNum, c.cands.Intersect(cands).ValueString())
		o.Removed = append(o.Removed, s)
	}
	o.Removed = compactCands(o.Removed)
}

func (o *Result) addHelpHouses(houses []*House) {
	s := []string{}
	for _, house := range houses {
		s = append(s, strHouse(house))
	}
	o.HelpHouses = append(o.HelpHouses, s)
}

func (o *Result) addHelpChains(chain string) {
	o.HelpChains = append(o.HelpChains, chain)
}
func (o *Result) addHelpValues(cs []*Cell) {
	s := []string{}
	for _, c := range cs {
		if c.given {
			s = append(s, strGivenCell(c.rowNum, c.colNum, c.value))
		} else {
			s = append(s, strChosenCell(c.rowNum, c.colNum, c.value))
		}
	}
	o.HelpValues = append(o.HelpValues, s)
}

func (o *Result) setHelpCands(cands set.Set, cells []*Cell) {
	s := []string{}
	for _, c := range cells {
		s = append(s, strCandsCell(c.rowNum, c.colNum, cands.ValueString()))
	}
	o.HelpCands = append(o.HelpCands, s)
}

func (o *Result) getHelpCandsGroup() HelpCandsGroup {
	group := HelpCandsGroup{}
	group.result = o
	group.num = len(o.HelpCands)
	o.HelpCands = append(o.HelpCands, []string{})
	return group
}

type HelpCandsGroup struct {
	num    int
	result *Result
}

func (o *Result) getHelpValuesGroup() HelpValuesGroup {
	group := HelpValuesGroup{}
	group.result = o
	group.num = len(o.HelpValues)
	o.HelpValues = append(o.HelpValues, []string{})
	return group
}

type HelpValuesGroup struct {
	num    int
	result *Result
}

func compactCands(s []string) []string {
	slices.Sort(s)
	pre := s[0]
	m := []string{}
	for _, cur := range s[1:] {
		if cur[:4] == pre[:4] {
			cc := []byte(pre[5:len(pre)-1] + cur[5:len(cur)-1])
			slices.Sort(cc)
			pre = fmt.Sprintf("%v[%v]", cur[0:4], string(slices.Compact(cc)))
		} else {
			if len(pre) > 6 {
				m = append(m, pre)
			}
			pre = cur
		}
	}
	if len(pre) > 6 {
		m = append(m, pre)
	}
	return m
}

func (g *HelpCandsGroup) add(cands set.Set, cells []*Cell) {
	s := g.result.HelpCands[g.num]
	for _, c := range cells {
		s = append(s, strCandsCell(c.rowNum, c.colNum, c.cands.Intersect(cands).ValueString()))
	}
	//fmt.Println("add", cells, s, compactCands(s))
	g.result.HelpCands[g.num] = compactCands(s)
}

func (g *HelpValuesGroup) add(cells []*Cell) {
	s := g.result.HelpValues[g.num]
	for _, c := range cells {
		if c.given {
			s = append(s, strGivenCell(c.rowNum, c.colNum, c.value))
		} else {
			s = append(s, strChosenCell(c.rowNum, c.colNum, c.value))
		}
	}
	g.result.HelpValues[g.num] = s
}

func (o *Result) addHelpCells(cells []*Cell) {
	s := []string{}
	for _, c := range cells {
		s = append(s, strCell(c.rowNum, c.colNum))
	}
	o.HelpCells = append(o.HelpCells, s)
}

func strCell(rowNum, colNum int) string {
	return fmt.Sprintf("R%vC%v", rowNum+1, colNum+1)
}

func (o *Result) setStrategyName(sname string) {
	o.StrategyName = sname
}

func strGivenCell(rowNum, colNum, value int) string {
	return fmt.Sprintf("R%vC%v{%v}", rowNum+1, colNum+1, value)
}

func strChosenCell(rowNum, colNum, value int) string {
	return fmt.Sprintf("R%vC%v(%v)", rowNum+1, colNum+1, value)
}

func strCandsCell[T any](rowNum, colNum int, cands T) string {
	return fmt.Sprintf("R%vC%v[%v]", rowNum+1, colNum+1, cands)
}

func strHouse(h *House) string {
	switch h.kind {
	case 0:
		return fmt.Sprintf("R%v", h.cells[0].rowNum+1)
	case 1:
		return fmt.Sprintf("C%v", h.cells[0].colNum+1)
	default:
		return fmt.Sprintf("B%v", h.cells[0].boxNum+1)
	}
}

type Techique func(*Grid) (bool, []*Result)

// ContainRemoved 检查结果集中是否包含特定的移除操作
